/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004-2005  Benoit Blanchon                      */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/


/******************************************************************/
/* Build configuration                                            */
/******************************************************************/

#define TRACE_LEVEL	2 /* warning */


/******************************************************************/
/* Includes                                                       */
/******************************************************************/

// module's interface
#include "HookFile.h"

// standard headers
#include <shlwapi.h> // for PathXxx
#include <tchar.h>

// ddk headers
#include <ddk/ntstatus.h>

// project's headers
#include "Assert.h"
#include "Hooks.h"
#include "SpyDllInt.h"
#include "strlcpy.h"
#include "Trace.h"


/******************************************************************/
/* Internal data                                                  */
/******************************************************************/

TCHAR	g_szSystemRoot[MAX_PATH] ;


/******************************************************************/
/* Ntdll stuffs                                                   */
/******************************************************************/

#define FileNameInformation	9
#define FileRenameInformation	10
#define FileLinkInformation	11

typedef struct {
  ULONG FileNameLength ;
  WCHAR FileName[1] ;
} FILE_NAME_INFORMATION ;

typedef struct {
  BOOL		ReplaceIfExists ;
  HANDLE	RootDirectory ;
  ULONG		FileNameLength ;
  WCHAR		FileName[1] ;
} FILE_LINK_RENAME_INFORMATION ;

NTSTATUS WINAPI NtQueryInformationFile (HANDLE, PIO_STATUS_BLOCK,
					PVOID, ULONG, ULONG) ;


/******************************************************************/
/* Internal functions                                             */
/******************************************************************/

BOOL HookFile_NtPathNameToDosPathName (HANDLE	hRootDirectory,
				       LPTSTR	szDosPath,
				       LPCTSTR	szNtPath) ;

BOOL HookFile_ChangePrefix (LPCTSTR szOldPath, LPCTSTR szPrefix, 
			    LPTSTR szNewPath, LPCTSTR szNewPrefix) ;


/******************************************************************/
/* Exported function                                              */
/******************************************************************/

BOOL HookFile_Init ()
{
  SHGetSpecialFolderPath (NULL, g_szSystemRoot, CSIDL_SYSTEM, FALSE) ;

  return TRUE ;
}


/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS WINAPI Hook_NtCreateFile (PHANDLE	FileHandle,
				   ACCESS_MASK	DesiredAccess,
				   POBJECT_ATTRIBUTES	ObjectAttributes,
				   PIO_STATUS_BLOCK	IoStatusBlock,
				   PLARGE_INTEGER	AllocationSize,
				   ULONG	FileAttributes,
				   ULONG	ShareAccess,
				   ULONG	CreateDisposition,
				   ULONG	CreateOptions,
				   PVOID	EaBuffer,
				   ULONG	EaLength) 
{
  {
    DWORD		nReaction = RULE_ACCEPT ;
    TCHAR		szFilePath[MAX_PATH] ;
   
    if( ObjectAttributes )
      TRACE_INFO (TEXT("%ls (0x%X)\n"), ObjectAttributes->ObjectName->Buffer, DesiredAccess) ;
    else
      TRACE_WARNING (TEXT("ObjectAttributes==NULL\n")) ;

    //TRACE_INFO (TEXT("NT path name : %s\n"), ObjectAttributes->ObjectName->Buffer) ;
    HookFile_NtPathNameToDosPathName (ObjectAttributes->RootDirectory,
				      szFilePath, ObjectAttributes->ObjectName->Buffer) ;
    //TRACE_INFO (TEXT("Dos path name : %s\n"), szFilePath) ;
    
    // log call  
    if( ( DesiredAccess & (GENERIC_WRITE|GENERIC_ALL) ) ||
	( CreateDisposition==FILE_SUPERSEDE ) ||
	( CreateDisposition==FILE_CREATE ) ||
	( CreateDisposition==FILE_OVERWRITE ) ||
	( CreateDisposition==FILE_OVERWRITE_IF)  ||
	( CreateOptions & (FILE_DELETE_ON_CLOSE)) ) 
	nReaction = SpyInt_CatchCall (REASON_FILE_WRITE, TEXT("s"), szFilePath) ;
    
    if( nReaction==RULE_ACCEPT && DesiredAccess&(GENERIC_READ|GENERIC_ALL) )
      nReaction = SpyInt_CatchCall (REASON_FILE_READ, TEXT("s"), szFilePath) ;

    //    if( DesiredAccess & (GENERIC_EXECUTE|GENERIC_ALL) )
    //      nReaction = SpyInt_CatchCall (REASON_SYS_EXECUTE, TEXT("s"), szFilePath) ;
    
    if( nReaction != RULE_ACCEPT ) 
      {
	IoStatusBlock->Status = STATUS_ACCESS_DENIED ;
	IoStatusBlock->Information = 0 ;
	
	return STATUS_ACCESS_DENIED ;
      }
  }

  JUMP_TO_STUB (HOOKS_NTCREATEFILE) ;
  ASSERT (0) ;
  return 0 ;
}

/******************************************************************/
/* Exported function :                                            */
/******************************************************************/

NTSTATUS WINAPI Real_NtCreateFile (PHANDLE	FileHandle,
				   ACCESS_MASK	DesiredAccess,
				   POBJECT_ATTRIBUTES	ObjectAttributes,
				   PIO_STATUS_BLOCK	IoStatusBlock,
				   PLARGE_INTEGER	AllocationSize,
				   ULONG	FileAttributes,
				   ULONG	ShareAccess,
				   ULONG	CreateDisposition,
				   ULONG	CreateOptions,
				   PVOID	EaBuffer,
				   ULONG	EaLength) 
{
  PROC	pfnStub = Hooks_GetStubAddress (HOOKS_NTCREATEFILE) ;
  
  if( ObjectAttributes )
    TRACE_INFO (TEXT("%ls (0x%X)\n"), ObjectAttributes->ObjectName->Buffer, DesiredAccess) ;
  else
    TRACE_WARNING (TEXT("ObjectAttributes==NULL\n")) ;

  ASSERT (pfnStub!=NULL) ;

  return (NTSTATUS)pfnStub (FileHandle, DesiredAccess, ObjectAttributes,
			    IoStatusBlock, AllocationSize, FileAttributes,
			    ShareAccess, CreateDisposition, CreateOptions,
			    EaBuffer, EaLength) ;    
}


/******************************************************************/
/* Exported function :                                            */
/******************************************************************/

NTSTATUS WINAPI Hook_NtOpenFile (PHANDLE	FileHandle,
				 ACCESS_MASK	DesiredAccess,
				 POBJECT_ATTRIBUTES	ObjectAttributes,
				 PIO_STATUS_BLOCK	IoStatusBlock,
				 PLARGE_INTEGER	AllocationSize,
				 ULONG		ShareAccess,
				 ULONG		OpenOptions) 
{
  {
    DWORD nReaction = RULE_ACCEPT ;
    TCHAR szFilePath[MAX_PATH] ;
    
    if( ObjectAttributes )
      TRACE_INFO (TEXT("%ls (0x%X)\n"), ObjectAttributes->ObjectName->Buffer, DesiredAccess) ;
    else
      TRACE_WARNING (TEXT("ObjectAttributes==NULL\n")) ;
    
    //TRACE_INFO (TEXT("NT path name : %s\n"), ObjectAttributes->ObjectName->Buffer) ;
    HookFile_NtPathNameToDosPathName (ObjectAttributes->RootDirectory,
				      szFilePath, ObjectAttributes->ObjectName->Buffer) ;
    //TRACE_INFO (TEXT("Dos path name : %s\n"), szFilePath) ;
    
    // log call  
    if( DesiredAccess & (GENERIC_WRITE|GENERIC_ALL) )
      nReaction = SpyInt_CatchCall (REASON_FILE_WRITE, TEXT("s"), szFilePath) ;
    
    if(  nReaction==RULE_ACCEPT && DesiredAccess&(GENERIC_READ|GENERIC_ALL) )
      nReaction = SpyInt_CatchCall (REASON_FILE_READ, TEXT("s"), szFilePath) ;

    //if( DesiredAccess & (GENERIC_EXECUTE|GENERIC_ALL) )
    //  nReaction = SpyInt_CatchCall (REASON_SYS_EXECUTE, TEXT("s"), szFilePath) ;
    
    if( nReaction != RULE_ACCEPT ) 
      {
	IoStatusBlock->Status = STATUS_ACCESS_DENIED ;
	IoStatusBlock->Information = 0 ;
	
	return STATUS_ACCESS_DENIED ;
      }
  }

  JUMP_TO_STUB (HOOKS_NTOPENFILE) ;
  ASSERT (0) ;
  return 0 ;
}

/******************************************************************/
/* Exported function :                                            */
/******************************************************************/

NTSTATUS WINAPI Real_NtOpenFile (PHANDLE	FileHandle,
				 ACCESS_MASK	DesiredAccess,
				 POBJECT_ATTRIBUTES	ObjectAttributes,
				 PIO_STATUS_BLOCK	IoStatusBlock,
				 PLARGE_INTEGER	AllocationSize,
				 ULONG	ShareAccess,
				 ULONG	OpenOptions) 
{
  PROC	pfnStub = Hooks_GetStubAddress (HOOKS_NTOPENFILE) ;
  
  if( ObjectAttributes )
    TRACE_INFO (TEXT("%ls (0x%X)\n"), ObjectAttributes->ObjectName->Buffer, DesiredAccess) ;
  else
    TRACE_WARNING (TEXT("ObjectAttributes==NULL\n")) ;

  ASSERT (pfnStub!=NULL) ;

  return (NTSTATUS)pfnStub (FileHandle, DesiredAccess, ObjectAttributes,
			    IoStatusBlock, AllocationSize,
			    ShareAccess, OpenOptions) ;    
}


/******************************************************************/
/* Exported function :                                            */
/******************************************************************/

NTSTATUS WINAPI Hook_NtDeleteFile (POBJECT_ATTRIBUTES	ObjectAttributes) 
{
  {
    DWORD nReaction ;
    TCHAR szFilePath[MAX_PATH] ;
    
    if( ObjectAttributes )
      TRACE_INFO (TEXT("%ls\n"), ObjectAttributes->ObjectName->Buffer) ;
    else
      TRACE_WARNING (TEXT("ObjectAttributes==NULL\n")) ;
    
    //TRACE_INFO (TEXT("NT path name : %s\n"), ObjectAttributes->ObjectName->Buffer) ;
    HookFile_NtPathNameToDosPathName (ObjectAttributes->RootDirectory,
				      szFilePath, ObjectAttributes->ObjectName->Buffer) ;
    //TRACE_INFO (TEXT("Dos path name : %s\n"), szFilePath) ;

    //TRACE_INFO (TEXT("PATH = %s\n"), szFilePath) ;
    
    // log call  
    nReaction = SpyInt_CatchCall (REASON_FILE_WRITE, TEXT("s"), szFilePath) ;
    
    if( nReaction == RULE_FEIGN ) 
      return STATUS_SUCCESS ;

    if( nReaction != RULE_ACCEPT ) 
      return STATUS_ACCESS_DENIED ;
  }

  JUMP_TO_STUB (HOOKS_NTDELETEFILE) ;
  ASSERT (0) ;
  return 0 ;
}


BOOL HookFile_NtPathNameToDosPathName (HANDLE	hRootDirectory,
				       LPTSTR	szDosPath,
				       LPCTSTR	szNtPath) 
{
  //  int i = 0 ;

  ASSERT (szDosPath!=NULL) ;

  if( !szNtPath ) {
    szDosPath[0] = 0 ;
    return TRUE ;
  }

  if( szNtPath[0]!=TEXT('\\') )
    {
  /*    IO_STATUS_BLOCK	iostatus ;
      NTSTATUS	nResult ;
      LPTSTR	szRootDir ;
      BYTE	pBuffer[1024] ;

      nResult = NtQueryInformationFile (hRootDirectory, &iostatus, 
					pBuffer, sizeof(pBuffer)-2,
					FileNameInformation) ;
      
      if( nResult==STATUS_SUCCESS )
	{
	  szRootDir = ((FILE_NAME_INFORMATION*)pBuffer)->FileName ;
	  szRootDir[((FILE_NAME_INFORMATION*)pBuffer)->FileNameLength/2] = 0 ;
	}
  */
      TCHAR szDir[MAX_PATH] = TEXT("") ;

      GetCurrentDirectory (1024, szDir) ;
      
      PathCombine (szDosPath, szDir, szNtPath) ;
    }
  else
    {
     /* if( szNtPath[0]==TEXT('\\') && szNtPath[1]==TEXT('?') && 
	  szNtPath[2]==TEXT('?') && szNtPath[3]==TEXT('\\') && 
	  szNtPath[5]==TEXT(':') )
	i = 4 ;
      
	_tcslcpy (szDosPath, szNtPath+i, MAX_PATH) ;*/
      
      HookFile_ChangePrefix (szNtPath, TEXT("\\??\\"), szDosPath, TEXT("")) 
	|| HookFile_ChangePrefix (szNtPath, TEXT("\\DosDevice\\"), szDosPath, TEXT("")) 
	|| HookFile_ChangePrefix (szNtPath, TEXT("\\SystemRoot"), szDosPath, g_szSystemRoot) 
	|| _tcslcpy (szDosPath, szNtPath, MAX_PATH) ;
    } 
   
  return TRUE ;
}


/******************************************************************/
/* Internal function                                              */
/******************************************************************/

BOOL HookFile_ChangePrefix (LPCTSTR szOldPath, LPCTSTR szPrefix, 
			    LPTSTR szNewPath, LPCTSTR szNewPrefix)
{
  if( _tcsnicmp(szOldPath, szPrefix, _tcslen(szPrefix)) )
    return FALSE ;
  
  _tcscpy (szNewPath, szNewPrefix) ;
  _tcscat (szNewPath, szOldPath+_tcslen(szPrefix)) ;

  return TRUE ;
}


/******************************************************************/
/* Exported function                                              */
/******************************************************************/

NTSTATUS WINAPI Hook_NtSetInformationFile (HANDLE	FileHandle,
					   PIO_STATUS_BLOCK  IoStatusBlock,
					   PVOID	FileInformation,
					   ULONG	Length,
					   ULONG	FileInformationClass)
{
  {
    DWORD nReaction = RULE_ACCEPT ;

    TRACE_INFO (TEXT("FileInformationClass = %d\n"), FileInformationClass) ;

    if( FileInformationClass==FileRenameInformation ||
	FileInformationClass==FileLinkInformation )
      {
	TCHAR		szNtPath[MAX_PATH] ;
	TCHAR		szDosPath[MAX_PATH] ;
	FILE_LINK_RENAME_INFORMATION	*pInfo = FileInformation ;

	memcpy (szNtPath, pInfo->FileName, pInfo->FileNameLength) ;
	szNtPath[pInfo->FileNameLength/sizeof(TCHAR)] = 0 ;

	HookFile_NtPathNameToDosPathName (pInfo->RootDirectory,
					  szDosPath, 
					  szNtPath) ;	

	TRACE_INFO (TEXT("File path = %s\n"), szDosPath) ;

	// log call  
	nReaction = SpyInt_CatchCall (REASON_FILE_WRITE, TEXT("s"), szDosPath) ;
      }

    if( nReaction == RULE_FEIGN ) 
      return STATUS_SUCCESS ;
    
    if( nReaction != RULE_ACCEPT ) 
      return STATUS_ACCESS_DENIED ;
  }

  JUMP_TO_STUB (HOOKS_NTSETINFORMATIONFILE) ;
  ASSERT (0) ;
  return 0 ;
}
